/* Nama File : point.cpp                            */
/* Deskripsi : Implementasi dari class Point        */
/* Anggota Kelompok : Dekha Anggareska 13510050		*/
/*                    Fadhil Muhtadin 13510070		*/
/*                    Muhammad Iqbal 13510064		*/
/*                    Yulius Nainggolan 13510090	*/

#include "point.h"
#include <iostream>
using namespace std;

    /* Empat Sekawan */
	Point::Point(){
    /* Konstruktor Point */
    /* I.S. Point belum tercipta */
    /* F.S. Point tercipta */
		// cout << "ctor Point" << endl;
	};

    Point::Point(int xi, int yi){
    /* Konstruktor Point dengan parameter */
    /* I.S. Point belum tercipta */
    /* F.S. Point tercipta */
		setX(xi);
		setY(yi);
		// cout << "ctor Point dengan Parameter" << endl;
	};

	Point::Point(const Point& PS){
    /* CCtor Point */
    /* I.S. Point terdefinisi */
    /* F.S. Point baru tercipta sebagai kopi Point sebelumnya */
		setX(PS.getX());
		setY(PS.getY());
		// cout << "cctor Point" << endl;
	};

	Point::~Point(){
    /* Destruktor Point */
	/* I.S. Point terdefinisi */
    /* F.S. Point dimusnahkan */
		// cout << "dtor Point" << endl;
	};

	Point& Point::operator=(const Point& PS){
    /* Melakukan assignment terhadap Point */
    /* I.S. Point terdefinisi */
    /* F.S. Point baru diassign dengan Point sebelumnya */
		setX(PS.getX());
		setY(PS.getY());
		// cout << "assignment Point" << endl;
		return *this;
	};

	void Point::setX(int xi){
    /* Melakukan Set terhadap nilai absis dari Point */
	/* I.S. Point terkonstruksi */
    /* F.S. Data absis bernilai xi */
		x = xi;
	};

	void Point::setY(int yi){
    /* Melakukan Set terhadap nilai ordinat dari Point */
	/* I.S. Point terkonstruksi */
    /* F.S. Data ordinat bernilai yi */
		y = yi;
	};

	int Point::getX() const{
    /* Mendapatkan nilai absis dari Point */
		return x;
	};

	int Point::getY() const{
    /* Mendapatkan nilai ordinat dari Point */
		return y;
	};
	
	int Point::IsUp (Point P2) {
	/* Mengembalikan 1 apabila P2 berada diatas P1, x kedua point sama */
		return((getX()==P2.getX())&&(getY()<P2.getY()));
	};
	
	int Point::IsDown (Point P2) { 
	/* Mengembalikan 1 apabila P2 berada dibawah P1, x kedua point sama */
		return((getX()==P2.getX())&&(getY()>P2.getY()));
	};
	
	int Point::IsRight (Point P2) {
	/* Mengembalikan 1 apabila P2 berada dikanan P1, y kedua point sama */
		return(((getY()==P2.getY())&&(getX()<P2.getX())));
	};
	
	int Point::IsLeft (Point P2) {
	/* Mengembalikan 1 apabila P2 berada dikiri P1, y kedua point sama */
		return((getY()==P2.getY())&&(getX()>P2.getX()));
	};
	
	int Point::IsUpRight(Point P2){
	/* Mengembalikan nilai 1 apabila P2 berada di sebelah kanan atas P1 */
		return((getY()<P2.getY())&&(getX()<P2.getX()));
	};

	int Point::IsUpLeft(Point P2){
	/* Mengembalikan nilai 1 apabila P2 berada di sebelah kiri atas P1 */
		return((getY()<P2.getY())&&(getX()>P2.getX()));
	};
	
	int Point::IsDownRight(Point P2){
	/* Mengembalikan nilai 1 apabila P2 berada di sebelah kanan bawah P1 */
		return((getY()>P2.getY())&&(getX()<P2.getX()));
	};
	
	int Point::IsDownLeft(Point P2){
	/* Mengembalikan nilai 1 apabila P2 berada di sebelah kiri bawah P1 */
		return((getY()>P2.getY())&&(getX()>P2.getX()));
	};
	
	int Point::IsAbove(Point Pc) {
	/* Mengembalikan nilai 1 bila P2 berada di atas P1, x kedua point tidak harus sama */
		return (getY()<Pc.getY());
	}
	
	
	int Point::IsBelow(Point Pc) {
	/* Mengembalikan nilai 1 bila P2 berada di bawah P1, x kedua point tidak harus sama */
		return (getY()>Pc.getY());
	}
	
	int Point::IsEq(Point Pc) {
	/* Mengembalikan nilai 1 bila P2 dan P1 merupakan point yang sama */
		return (getX() == Pc.getX() && getY() == Pc.getY());
	}
	
	int Point::isBetween(Point Pa, Point Pb) {
	/* Mengembalikan nilai 1 bila P1 segaris dengan Pa dan Pb dan berada diantara Pa dan Pb */
		float area;
		area = ((getX()-Pa.getX())*(Pb.getY()-Pa.getY()) - ((getY()-Pa.getY())*(Pb.getX()-Pa.getX())))/2;
		if (area == 0) {
			if (((IsUp(Pa) && IsDown(Pb)) || (IsUp(Pb) && IsDown(Pa)))
				|| ((IsRight(Pa) && IsLeft(Pb)) || (IsRight(Pb) && IsLeft(Pa)))
				|| ((IsUpRight(Pa) && IsDownLeft(Pb)) || (IsUpRight(Pb) && IsDownLeft(Pa)))
				|| ((IsUpLeft(Pa) && IsDownRight(Pb)) || (IsUpLeft(Pb) && IsDownRight(Pa))))
				{
					return 1;
				}
			else {return 0;}
		}
		else {
			return 0;
		}
	}
	
	int Point::isBoundaryIntersect(Point P3, Point P4) {
	/* Mengembalikan nilai sesuai tipe perpotongan garis thisPoint-P1 dengan P3-P4 */
	/* 0 bila tidak berpotongan */
	/* 1 bila berpotongan */
	/* 4 bila thisPoint berada pada sisi bidang */
	/* 5 bila titik perpotongan adalah titik sudut dan titik perpotongan merupakan titik paling bawah dari garis P3-P4 */
	/* 6 bila titik perpotongan adalah titik sudut dan titik perpotongan merupakan titik paling atas dari garis P3-P4 */
	/* 7 bila thisPoint merupakan salah satu titik sudut */
		Point P1 (-1, getY());
		//Line1
        float A1 = getY() - P1.getY();
        float B1 = P1.getX() - getX();
        float C1 = A1*P1.getX() + B1*P1.getY();
		//Line2
        float A2 = P4.getY() - P3.getY();
        float B2 = P3.getX() - P4.getX();
        float C2 = A2*P3.getX() + B2*P3.getY();
		
		float det = A1*B2 - A2*B1;
        if (det == 0) //paralel
        {
			if ((IsRight(P3) && IsLeft(P4)) || (IsRight(P4) && IsLeft(P3)) || IsEq(P3) || IsEq(P4)) {
				return 4;
			}
			else {return 0;}
        }
        else // tidak paralel
        {
            float x = (B2*C1 - B1*C2)/det;
            float y = (A1 * C2 - A2 * C1)/det;
			if (x > getX()) {
				return 0;
			}
			else if (IsEq(P3) || IsEq(P4)) {
				return 7;
			}
			else {
				Point PTemp ((int) x, (int) y);
				if ((PTemp.IsEq(P3) && PTemp.IsAbove(P4)) || (PTemp.IsEq(P4) && PTemp.IsAbove(P3))) {
					return 5;
				}
				else if ((PTemp.IsEq(P3) && PTemp.IsBelow(P4)) || (PTemp.IsEq(P4) && PTemp.IsBelow(P3))) {
					return 6;
				}
				else if (getX() == x && getY() == y) {
					if (PTemp.isBetween(P3,P4)) {
						return 4;
					}
					return 0;
				}
				else if ((IsAbove(P3) && IsAbove(P4)) || (IsBelow(P3) && IsBelow(P4))) {
					return 0;
				}
				else {return 1;}
			}
        }
	};

	ostream& operator<<(ostream& out, Point& PS){
    /* Mencetak Point ke layar */
	/* I.S. Point terdefinisi */
    /* F.S. Point tercetak di layar */
		return out << "(" << PS.getX() << "," << PS.getY() << ")" << endl;
	};
